package com.jwater.core.manager.common.deploy;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;

import com.jwater.core.manager.common.model.WorkerInfo;
import com.jwater.core.manager.worker.assign.WorkerEnv;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jwater.core.common.S;
import com.jwater.core.common.param.ParamMap;
import com.jwater.core.common.xml.XmlConfig;
import com.jwater.core.manager.common.ManagerException;
import com.jwater.core.manager.common.conf.WorkerConfParse;
import com.jwater.core.utils.ZipUtils;
import com.jwater.launch.DeployPathUtils;
import com.jwater.launch.HomePath;

import jwater.org.apache.commons.io.FileUtils;
import jwater.org.apache.commons.io.FilenameUtils;
import jwater.org.springframework.util.StringUtils;

/**
 * @description: 安装包部署支持类
 * @author: jnner
 * @createDate: 2021/2/5
 * @version: 1.0
 */
public class DeployUtils {
    protected static Logger logger = LoggerFactory.getLogger(DeployUtils.class);

    /**
     * 部署安装包
     *
     * @param fileName   文件名称
     * @param dst        安装包目标路径
     * @param regionName 部署到哪个regionName
     * @param deployName 安装包名称
     * @param keepConf   是否保留上次配置
     * @param version    当前部署包版本
     * @param path       部署包菜单路径
     */
    public static void deploy(String fileName, String dst, String regionName, String deployName, String keepConf,
                              long version, String path) {
        File file = new File(dst);
        if (!file.exists()) {
            throw new ManagerException("deploy file not exist for:" + dst);
        }
        try {
            if (fileName.endsWith(".jar")) {
                deployForJar(fileName, file, regionName, deployName, version);
            } else if (fileName.endsWith(".zip")) {
                deployForZip(fileName, file, regionName, keepConf, version, path);
            } else {
                throw new ManagerException("deploy file type not support for:" + fileName);
            }
        } catch (Exception e) {
            throw new ManagerException("deploy error for:" + fileName, e);
        } finally {
            FileUtils.deleteQuietly(file);
        }
    }

    /**
     * 部署jar包
     *
     * @param fileName
     * @param dstFile
     * @param regionName
     * @param deployName
     * @param version
     * @throws IOException
     */
    public static void deployForJar(String fileName, File dstFile, String regionName, String deployName, long version)
            throws IOException {
        if (StringUtils.isEmpty(deployName)) {
            throw new ManagerException("deploy name can not empty for:" + fileName);
        }
        // 优先采用替换模式，如果存在直接替换，如果不存在，直接放到lib目录下
        File deployPath = WorkerConfParse.getDeployPath(regionName, deployName);
        List<File> allJars = new ArrayList<File>();
        listJars(allJars, deployPath, true);
        boolean existJar = false;
        for (File jar : allJars) {
            if (fileName.equals(jar.getName())) {
                File newJar = new File(jar.getParentFile(),
                        FilenameUtils.getBaseName(fileName) + "--" + version + ".jar");
                FileUtils.copyFile(dstFile, newJar);
                existJar = true;
            }
        }
        if (!existJar) {
            File newJar = new File(deployPath + "/lib", fileName);
            FileUtils.copyFile(dstFile, newJar);
        }
    }

    /**
     * 罗列所有的jar包
     *
     * @param list
     * @param dir
     * @param recursive
     */
    private static void listJars(List<File> list, File dir, boolean recursive) {
        File[] files = dir.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                if (recursive) {
                    listJars(list, file, recursive);
                }
            } else {
                if (file.getName().endsWith(".jar")) {
                    list.add(file);
                }
            }
        }
    }

    /**
     * 部署zip包
     *
     * @param fileName
     * @param dstFile
     * @param regionName
     * @param keepConf
     * @param version
     * @param path
     * @throws IOException
     */
    public static void deployForZip(String fileName, File dstFile, String regionName, String keepConf, long version,
                                    String path) throws IOException {
        String deployName = FilenameUtils.getBaseName(fileName);
        File oldDeployPath = WorkerConfParse.getDeployPath(regionName, deployName);
        String rootName = ZipUtils.getRootName(dstFile);
        File deployPath = new File(HomePath.getRegionDeploy(regionName) + deployName + "/" + version);
        if (StringUtils.isEmpty(rootName)) {
            deployPath.mkdirs();
            ZipUtils.unzip(dstFile, deployPath);
        } else {
            File temp = new File(HomePath.getTmpFilePath(), UUID.randomUUID().toString());
            ZipUtils.unzip(dstFile, temp);
            if (!deployPath.getParentFile().exists()) {
                deployPath.getParentFile().mkdirs();
            }
            FileUtils.moveDirectory(new File(temp, rootName), deployPath);
            FileUtils.deleteDirectory(temp);
        }
        updateInfoXml(regionName, deployName, version, path);
        if (oldDeployPath != null && "true".equals(keepConf)) {
            copyOldConf(oldDeployPath, deployPath);
        }
        deleteOld(regionName, deployName);
    }

    /**
     * 移除老的部署包，当个数大于5个时开始移除
     *
     * @param regionName
     * @param deployName
     */
    private static void deleteOld(String regionName, String deployName) {
        File dir = new File(HomePath.getRegionDeploy(regionName), deployName);
        if (dir.listFiles().length > 6) {
            File min = DeployPathUtils.getMinVersionDir(dir);
            FileUtils.deleteQuietly(min);
        }
    }

    /**
     * 更新jwater.info.xml文件
     *
     * @param regionName
     * @param deployName
     * @param version
     * @param path
     * @throws IOException
     */
    public static void updateInfoXml(String regionName, String deployName, long version, String path)
            throws IOException {
        ParamMap infoProps = WorkerConfParse.parseInfo(regionName, deployName);
        infoProps.put("version", version);
        infoProps.put("path", path);
        StringBuilder sb = new StringBuilder();
        sb.append("<jwater>\r\n<props>");
        for (Entry<String, Object> en : infoProps.entrySet()) {
            sb.append("<prop key=\"" + en.getKey() + "\" value=\"" + en.getValue() + "\" />");
        }
        sb.append("</props>\r\n</jwater>");
        File file = new File(HomePath.getRegionDeploy(regionName) + deployName + "/jwater.info.xml");
        FileUtils.write(file, sb.toString(), S.charset);
    }

    public static void updateInfoProp(String regionName, String deployName, String key, String value)
            throws IOException {
        ParamMap infoProps = WorkerConfParse.parseInfo(regionName, deployName);
        infoProps.put(key, value);
        StringBuilder sb = new StringBuilder();
        sb.append("<jwater>\r\n<props>");
        for (Entry<String, Object> en : infoProps.entrySet()) {
            sb.append("<prop key=\"" + en.getKey() + "\" value=\"" + en.getValue() + "\" />");
        }
        sb.append("</props>\r\n</jwater>");
        File file = new File(HomePath.getRegionDeploy(regionName) + deployName + "/jwater.info.xml");
        FileUtils.write(file, sb.toString(), S.charset);
    }

    /**
     * 获取配置目录
     *
     * @param deployPath
     * @return
     */
    public static List<String> getConfDirs(File deployPath) {
        List<String> confDirs = new ArrayList<String>();
        confDirs.add("conf");
        String workerXmlPath = HomePath.getWorkerXmlPath(deployPath.getPath());
        try {
            XmlConfig config = new XmlConfig();
            config.loadConfig(workerXmlPath);
            List<ParamMap> groups = config.getParamMapsByPath("jwater/workerGroup");
            if (groups == null) {
                return confDirs;
            }
            List<ParamMap> props = new ArrayList<>();
            for (ParamMap group : groups) {
                List<ParamMap> workers = group.getParamMaps("worker");
                for (ParamMap worker : workers) {
                    ParamMap prop = WorkerConfParse.parseWorkerProps(worker);
                    props.add(prop);
                }
            }
            for (ParamMap prop : props) {
                String confDir = prop.getString("confDir");
                if (!StringUtils.isEmpty(confDir)) {
                    for (String item : confDir.split(",")) {
                        confDirs.add(item);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("parse error for:" + workerXmlPath, e);
        }
        return confDirs;
    }

    /**
     * 复制老的配置到新的安装路径下
     *
     * @param oldDeployPath
     * @param deployPath
     * @throws IOException
     */
    private static void copyOldConf(File oldDeployPath, File deployPath) throws IOException {
        List<String> confDirs = getConfDirs(deployPath);
        for (String confDir : confDirs) {
            File conf = new File(confDir);
            if (conf.exists()) {
                FileUtils.deleteQuietly(conf);
            }
            File oldConf = new File(oldDeployPath, confDir);
            if (oldConf.exists()) {
                FileUtils.copyDirectory(oldConf, conf);
            }
        }
    }

    /**
     * 移除安装包
     *
     * @param regionName
     * @param deployName
     * @throws Exception
     */
    public static void removeDeploy(String regionName, String deployName) throws Exception {
        String tmpDeployPath = HomePath.getTmpDeployPath(regionName, deployName);
        File deployPath = new File(HomePath.getRegionDeploy(regionName), deployName);
        if (deployPath.exists()) {
            FileUtils.moveDirectory(deployPath, new File(tmpDeployPath, String.valueOf(System.currentTimeMillis())));
            File dir = new File(tmpDeployPath);
            if (dir.listFiles().length > 5) {
                File min = DeployPathUtils.getMinVersionDir(dir);
                FileUtils.deleteQuietly(min);
            }
        }
    }

    public static Map<String, String> getWorkerEnvs(String regionName, String deployName, String groupName, String workerName) {
        Map<String, String> envs = new HashMap<>();
        WorkerConfParse workerConfParse = new WorkerConfParse();
        workerConfParse.parseDeploy(regionName, deployName);
        WorkerInfo workerInfo = workerConfParse.getWorkerInfo(regionName, deployName, groupName, workerName);
        if (workerInfo == null) {
            return envs;
        }
        for (Map.Entry<String, Object> en : workerInfo.getProps().entrySet()) {
            if (en.getKey().startsWith("env.")) {
                envs.put(en.getKey().replace("env.",""), en.getValue()+"");
            }
        }
        return envs;
    }
}
